/**  \file
 *   \brief libreria di comunicazione socket AF_UNIX
 *
 *    \author Marco Luisi
*/

#include "comsock.h"
#include "cars.h"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>


/** Crea una socket AF_UNIX
 *  \param  path pathname della socket
 *
 *  \retval s    il file descriptor della socket  (s>0)
 *  \retval -1   in altri casi di errore (setta errno)
 *               errno = E2BIG se il nome eccede UNIX_PATH_MAX
 *
 *  in caso di errore ripristina la situazione inziale: rimuove eventuali socket create e chiude eventuali file descriptor rimasti aperti
 */
int createServerChannel(char* path){

	int channel = 0;
	struct sockaddr_un sock_a;

	/* controllo correttezza del path */
	if (!path){
		errno = EINVAL;
		return(-1);
	}

	if(strlen(path) >= UNIX_PATH_MAX) {
		errno = E2BIG;
		return(-1);
	}

	sock_a.sun_family = AF_UNIX;
	strncpy(sock_a.sun_path,path,UNIX_PATH_MAX);
	channel = socket(AF_UNIX, SOCK_STREAM, 0);
	EC_M1(channel);
	EC_M1(bind(channel,(struct sockaddr *)&sock_a, sizeof(sock_a)));
	EC_M1(listen(channel,SOMAXCONN));

	return channel;

}

/** Chiude una socket
 *   \param s file descriptor della socket
 *
 *   \retval 0  se tutto ok,
 *   \retval -1  se errore (setta errno)
 */
int closeSocket(int s){
	return close(s);
}


/** accetta una connessione da parte di un client
 *  \param  s socket su cui ci mettiamo in attesa di accettare la connessione
 *
 *  \retval  c il descrittore della socket su cui siamo connessi
 *  \retval  -1 in casi di errore (setta errno)
 */
int acceptConnection(int s){

	return(accept(s, NULL,0));

}


/** legge un messaggio dalla socket --- attenzione si richiede che il messaggio sia adeguatamente spacchettato e trasferito nella struttura msg
 *  \param  sc  file descriptor della socket
 *  \param msg  struttura che conterra' il messagio letto
 *		(deve essere allocata all'esterno della funzione,
 *		tranne il campo buffer)
 *
 *  \retval lung  lunghezza del buffer letto, se OK
 *  \retval  -1   in caso di errore (setta errno)
 *                 errno = ENOTCONN se il peer ha chiuso la connessione
 *                   (non ci sono piu' scrittori sulla socket)
 *
 */
int receiveMessage(int sc, message_t * msg){

	unsigned int bytes = 0;
	/*Controllo del tipo del messaggio*/
	bytes = read(sc, &(msg->type), sizeof(char));
	if(bytes == 0){
		errno = ENOTCONN;
		return (-1);
	}
	if(bytes == -1){
		return (-1);
	}
	/*Lettura della lunghezza del messaggio*/
	bytes = 0;

	while( (bytes += read(sc, &(msg->length), sizeof(unsigned int) ) )< sizeof(unsigned int)){
		if (bytes == 0){//ATTENZIONE QUI lo zero potrebbe far saltare?!
			errno = ENOTCONN;
			return(-1);//QUI ERA SEOF OCCHIO!!!!!!!!!!!!!!
		}
		if (bytes == -1)
			return(-1);
	}

	msg->buffer = calloc(msg->length + 1, sizeof(char));
	if (msg->buffer == NULL){
		printf("COMSOCK errore allocazione buffer receive\n");
		//uscita?!!!!!!!!!!!!!!!!!!!!
	}

	/*Riempimento del buffer*/
	while( (bytes += read(sc, msg->buffer, msg->length)) < msg->length){
			if(bytes == 0){
				errno = ENOTCONN;
			return -1;
			}		if(bytes == -1){
				printf("COMSOCK receive errore riempimento buffer\n");

				return -1;
			}
	}

	return bytes;
}


/** scrive un messaggio sulla socket --- attenzione si richiede che il messaggio venga scritto con un'unica write dopo averlo adeguatamente impacchettato
 *   \param  sc file descriptor della socket
 *   \param msg struttura che contiene il messaggio da scrivere
 *
 *   \retval  n    il numero di caratteri inviati (se scrittura OK)
 *   \retval -1   in caso di errore (setta errno)
 *                 errno = ENOTCONN se il peer ha chiuso la connessione
 *                   (non ci sono piu' lettori sulla socket)
 */
int sendMessage(int sc, message_t *msg){

	int bytes = 0;

	if (msg == NULL){
			errno = EINVAL;
			return -1;
		}
	printf("sendmessage a %d di %d byte TIPO %s\n",sc,msg->length,&(msg->type));
	/*Invio "metadati" del messaggio*/
	bytes = write(sc,&(msg->type), sizeof(char));
	//prima faceva distinzione tra -1 e seof
	if(bytes < sizeof(char)){
		return (-1);
	}

	bytes = write(sc,&(msg->length), sizeof(unsigned int));
	if(bytes == -1){
		return (-1);
	}
	if(bytes < sizeof(unsigned int)){
			return(-1);
	}
	if(&(msg->length) == 0){
		printf("sendmsg msg vuoto, esce\n");
		return(bytes);

	}
	//rifare la somma dei byte!!!!!!

	/*Invio del payload del messaggio*/
	bytes = write(sc, msg->buffer, msg->length);
	printf("sendmsg write del buffer\n");

	if( bytes < msg->length){
		return (-1);
	}

	printf("sendmsg fine!\n");

	return bytes;

}


/** crea una connessione all socket del server. In caso di errore funzione tenta NTRIALCONN volte la connessione (a distanza di 1 secondo l'una dall'altra) prima di ritornare errore.
 *   \param  path  nome del socket su cui il server accetta le connessioni
 *
 *   \return fd il file descriptor della connessione
 *            se la connessione ha successo
 *   \retval -1 in caso di errore (setta errno)
 *               errno = E2BIG se il nome eccede UNIX_PATH_MAX
 *
 *  in caso di errore ripristina la situazione inziale: rimuove eventuali socket create e chiude eventuali file descriptor rimasti aperti
 */
int openConnection(char* path){

	int trials;
	int sc;
	struct sockaddr_un sa;

	if(path == NULL){
		errno = EINVAL;
		return(-1);
	}

	if(strlen(path) >= UNIX_PATH_MAX){
		errno = E2BIG;
		return(-1);
	}

	sa.sun_family = AF_UNIX;
	strncpy(sa.sun_path, path, UNIX_PATH_MAX);

	sc = socket(AF_UNIX, SOCK_STREAM, 0);
	EC_M1(sc);
	/*Si tenta la connessione al socket*/
	for(trials = 0; trials < NTRIALCONN; trials++){
		/*Connect riuscita*/
		if (connect(sc, (struct sockaddr *) &sa, sizeof(sa)) == 0){
			return(sc);
		}
		sleep(1);
	}
	/*Impossibile stabilire una connessione al socket, errno settato da connect*/

	//GESTIONE SOCK DA CANCELLARE?!?!?!?!!!!!!!!!!!!!!


	return(-1);


}




